﻿using System.Globalization;
using System.Reflection;
using System.Text.Json.Serialization;
using Newtonsoft.Json;

namespace Devonline.Core;

public static class CollectionExtensions
{
    /// <summary>
    /// 集合是否为空或 null
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static bool IsNullOrEmpty<T>(this IEnumerable<T> source) => source == null || (!source.Any());
    /// <summary>
    /// 集合是否不为空或 null
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static bool IsNotNullOrEmpty<T>(this IEnumerable<T> source) => source != null && source.Any();

    /// <summary>
    /// 集合乱序
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source"></param>
    /// <returns></returns>
    public static IEnumerable<T> ToShuffle<T>(this IEnumerable<T> source)
    {
        var count = source.Count();
        var temps = source.ToList();
        var targets = new List<T>(count);
        var random = new Random();
        while (count != 0)
        {
            var index = random.Next(0, count);
            targets.Add(temps[index]);
            temps.RemoveAt(index);
            count--;
        }

        return targets;
    }

    /// <summary>
    /// Collection ToString extension method, concatenate all collection object to a format string 
    /// </summary>
    /// <typeparam name="T">collection object type</typeparam>
    /// <param name="collection">collection object instance</param>
    /// <param name="format">object format, default is "{0}"</param>
    /// <param name="spliter">object spliter, default is "; "</param>
    /// <returns>concatenate all collection object to a format string</returns>
    public static string ToString<T>(this IEnumerable<T> collection, string spliter = AppSettings.DEFAULT_SPLITER_STRING, string format = AppSettings.DEFAULT_FORMAT_STRING)
    {
        var result = new List<string>();
        foreach (var item in collection)
        {
            if (item != null)
            {
                var value = string.Format(CultureInfo.CurrentCulture, format, item);
                if (!string.IsNullOrWhiteSpace(value))
                {
                    result.Add(value);
                }
            }
        }

        return string.Join(spliter, result);
    }

    /// <summary>
    /// 从类似于 url 的字符串中截取参数列表到键值对
    /// </summary>
    /// <param name="value">原始字符串</param>
    /// <param name="outer">外部连接符</param>
    /// <param name="inner">内部连接符</param>
    /// <returns></returns>
    public static ICollection<KeyValuePair<string, string>> ToKeyValuePairs(this string value, string outer = AppSettings.DEFAULT_OUTER_SPLITER, string inner = AppSettings.DEFAULT_INNER_SPLITER)
    {
        var values = new Dictionary<string, string>();
        var pairs = value.Split(outer, StringSplitOptions.RemoveEmptyEntries);
        if (pairs != null && pairs.Length > 0)
        {
            foreach (var pair in pairs)
            {
                if (pair.IsNotNullOrEmpty())
                {
                    var keyValue = pair.Split(inner, StringSplitOptions.RemoveEmptyEntries);
                    var key = keyValue[0];
                    if (key.IsNotNullOrEmpty())
                    {
                        values.Add(key, keyValue[1] ?? string.Empty);
                    }
                }
            }
        }

        return values;
    }
    /// <summary>
    /// 将键值对转换为类似于 url 的包含参数的字符串
    /// </summary>
    /// <param name="keyValuePairs">键值对</param>
    /// <param name="outer">外部连接符</param>
    /// <param name="inner">内部连接符</param>
    /// <param name="useCamelCase">字段名使用驼峰法</param>
    /// <returns></returns>
    public static string ToString(this ICollection<KeyValuePair<string, object>> keyValuePairs, string outer = AppSettings.DEFAULT_OUTER_SPLITER, string inner = AppSettings.DEFAULT_INNER_SPLITER, bool useCamelCase = true)
    {
        var strings = new List<string>();
        foreach (var keyValue in keyValuePairs)
        {
            if (keyValue.Value != null)
            {
                strings.Add((useCamelCase ? keyValue.Key.FirstCharToLower() : keyValue.Key) + inner + keyValue.Value);
            }
        }

        return string.Join(outer, strings);
    }
    /// <summary>
    /// 将键值对转换为对象
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="keyValuePairs"></param>
    /// <param name="useCamelCase">字段名使用驼峰法</param>
    /// <returns></returns>
    public static T ToObject<T>(this ICollection<KeyValuePair<string, object>> keyValuePairs, bool useCamelCase = true) where T : class, new()
    {
        var t = new T();
        var type = typeof(T);
        foreach (var keyValue in keyValuePairs)
        {
            var propertyInfo = type.GetProperty(useCamelCase ? keyValue.Key.FirstCharToUpper() : keyValue.Key);
            if (propertyInfo != null)
            {
                propertyInfo.SetValue(t, keyValue.Value == null ? null : Convert.ChangeType(keyValue.Value, propertyInfo.PropertyType, CultureInfo.CurrentCulture));
            }
        }

        return t;
    }
    /// <summary>
    /// 将对象公共成员属性转为键值对形式
    /// </summary>
    /// <typeparam name="T">转换对象类型</typeparam>
    /// <param name="obj">转换对象</param>
    /// <param name="useCamelCase">字段名使用驼峰法</param>
    /// <param name="useJsonProperty">使用 JsonProperty 特性指定的字段名</param>
    /// <returns></returns>
    public static ICollection<KeyValuePair<string, object>> ToKeyValuePairs(this object obj, bool useCamelCase = true, bool useJsonProperty = true)
    {
        var keyValuePairs = new List<KeyValuePair<string, object>>();
        var propertyInfos = obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
        if (propertyInfos.Length > 0)
        {
            foreach (var propertyInfo in propertyInfos)
            {
                var value = propertyInfo.GetValue(obj);
                if (value != null)
                {
                    var key = string.Empty;
                    if (useJsonProperty)
                    {
                        key = propertyInfo.GetAttributeValue<JsonPropertyAttribute, string>(nameof(JsonPropertyAttribute.PropertyName)) ?? propertyInfo.GetAttributeValue<JsonPropertyNameAttribute, string>(nameof(JsonPropertyAttribute.PropertyName));
                    }

                    key ??= (useCamelCase ? propertyInfo.Name.FirstCharToLower() : propertyInfo.Name);
                    keyValuePairs.Add(new KeyValuePair<string, object>(key, value));
                }
            }
        }

        return keyValuePairs;
    }

    /// <summary>
    /// 添加或更新键值对
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="this"></param>
    /// <param name="key">键</param>
    /// <param name="value">值</param>
    /// <returns></returns>
    public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue value)
    {
        if (@this.ContainsKey(key))
        {
            @this[key] = value;
        }
        else
        {
            @this.Add(new KeyValuePair<TKey, TValue>(key, value));
        }

        return @this[key];
    }
    /// <summary>
    /// 添加或更新键值对
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="this"></param>
    /// <param name="key">键</param>
    /// <param name="addValue">添加时的值</param>
    /// <param name="updateValueHanlder">更新时的操作</param>
    /// <returns></returns>
    public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, TValue addValue, Func<TKey, TValue, TValue> updateValueHanlder)
    {
        if (@this.ContainsKey(key))
        {
            @this[key] = updateValueHanlder(key, @this[key]);
        }
        else
        {
            @this.Add(new KeyValuePair<TKey, TValue>(key, addValue));
        }

        return @this[key];
    }
    /// <summary>
    /// 添加或更新键值对
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    /// <param name="this"></param>
    /// <param name="key">键</param>
    /// <param name="addValueHanlder">添加时的操作</param>
    /// <param name="updateValueHanlder">更新时的操作</param>
    /// <returns></returns>
    public static TValue AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> @this, TKey key, Func<TKey, TValue> addValueHanlder, Func<TKey, TValue, TValue> updateValueHanlder)
    {
        if (@this.ContainsKey(key))
        {
            @this[key] = updateValueHanlder(key, @this[key]);
        }
        else
        {
            @this.Add(new KeyValuePair<TKey, TValue>(key, addValueHanlder(key)));
        }

        return @this[key];
    }
    /// <summary>
    /// 移除符合条件的元素
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="this"></param>
    /// <param name="where"></param>
    public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where) => @this.Where(where).ToList().ForEach(x => @this.Remove(x));

    /// <summary>
    /// 添加多个元素
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="this"></param>
    /// <param name="values"></param>
    public static void AddRange<T>(this ICollection<T> @this, IEnumerable<T> values)
    {
        foreach (var value in values)
        {
            @this.Add(value);
        }
    }
    /// <summary>
    /// 添加符合条件的多个元素
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="this"></param>
    /// <param name="predicate"></param>
    /// <param name="values"></param>
    public static void AddRange<T>(this ICollection<T> @this, Func<T, bool> predicate, params T[] values)
    {
        foreach (var value in values)
        {
            if (predicate(value))
            {
                @this.Add(value);
            }
        }
    }
    /// <summary>
    /// 添加不重复的元素
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="this"></param>
    /// <param name="values"></param>
    public static void AddRangeIfNotContains<T>(this ICollection<T> @this, params T[] values)
    {
        foreach (T value in values)
        {
            if (!@this.Contains(value))
            {
                @this.Add(value);
            }
        }
    }

    /// <summary>
    /// 原始对象集合转为目标对象集合
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <typeparam name="TTarget"></typeparam>
    /// <param name="sources"></param>
    /// <param name="convert"></param>
    /// <returns></returns>
    public static IEnumerable<TTarget>? ConvertAll<TSource, TTarget>(this IEnumerable<TSource> sources, Func<TSource, TTarget>? convert = null)
    {
        var sourceType = sources.GetType().GetElementType();
        var targetType = typeof(TTarget);

        //输入模型类型和输出模型类型不匹配时, 进行数据转换, 依据当前流程节点配置的 schame 进行转换
        if (sources == null || (!sources.Any()) || sources is IEnumerable<TTarget>)
        {
            return sources as IEnumerable<TTarget>;
        }

        // 整体转换不可用, 因为转换不了内部会抛异常, 导致整个数据转换失败
        // Data = Data.ToList().ConvertAll(ConvertModel);
        var targets = new List<TTarget>();
        Parallel.ForEach(sources, source =>
        {
            if (source != null)
            {
                TTarget target = (convert != null) ? convert(source) : source.CopyTo<TTarget>();
                if (target != null)
                {
                    lock (targets)
                    {
                        targets.Add(target);
                    }
                }
            }
        });

        return targets;
    }

    /// <summary>
    /// 返回一个集合的新的引用, 并据参数清空旧的集合
    /// TODO 有线程问题
    /// </summary>
    /// <typeparam name="T">原始集合泛型类型</typeparam>
    /// <param name="data">原始集合</param>
    /// <param name="count"></param>
    /// <returns></returns>
    public static ICollection<T>? GetFromCollection<T>(this ICollection<T> data, int count = 0)
    {
        ICollection<T>? list = null;
        if (data.Count > 0)
        {
            lock (data)
            {
                if (data.Count > 0)
                {
                    var all = false;
                    if (count <= 0 || count >= data.Count)
                    {
                        count = data.Count;
                        all = true;
                    }

                    list = all ? data.ToArray() : data.Take(count).ToArray();
                    if (all)
                    {
                        data.Clear();
                    }
                    else
                    {
                        foreach (var t in list)
                        {
                            data.Remove(t);
                        }
                    }
                }
            }
        }

        return list;
    }
    /// <summary>
    /// 返回队列的 count 个元素副本，并根据参数决定是否要清空集合
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="queue"></param>
    /// <param name="count"></param>
    /// <returns></returns>
    public static ICollection<T>? GetFromQueue<T>(this Queue<T> queue, int count = 0)
    {
        ICollection<T>? list = null;
        if (queue.Count > 0)
        {
            lock (queue)
            {
                if (queue.Count > 0)
                {
                    var all = false;
                    if (count <= 0 || count >= queue.Count)
                    {
                        count = queue.Count;
                        all = true;
                    }

                    if (all)
                    {
                        list = queue.ToArray();
                        queue.Clear();
                    }
                    else
                    {
                        list = new T[count];
                        for (int i = 0; i < count; i++)
                        {
                            list.Add(queue.Dequeue());
                        }
                    }
                }
            }
        }

        return list;
    }
    /// <summary>
    /// 将集合中所有元素放入新队列中并返回
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="data"></param>
    /// <returns></returns>
    public static Queue<T> ToQueue<T>(this ICollection<T> data)
    {
        var queue = new Queue<T>();
        if (data.Count != 0)
        {
            Parallel.ForEach(data, t =>
            {
                lock (queue)
                {
                    queue.Enqueue(t);
                }
            });
        }

        return queue;
    }
    /// <summary>
    /// 将集合中的数据并行放入队列
    /// </summary>
    /// <returns></returns>
    public static void MoveToQueue<T>(this ICollection<T> data, Queue<T> queue)
    {
        if (data.Count > 0)
        {
            var list = data.IsReadOnly ? data : data.GetFromCollection();
            if (list != null && list.Count > 0)
            {
                Parallel.ForEach(list, t =>
                {
                    lock (queue)
                    {
                        queue.Enqueue(t);
                    }
                });
            }
        }
    }
    /// <summary>
    /// 从队列中弹出并返回一个值
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="queue"></param>
    /// <returns></returns>
    public static T? GetFromQueue<T>(this Queue<T> queue)
    {
        T? t = default;
        if (queue.Count > 0)
        {
            lock (queue)
            {
                if (queue.Count != 0)
                {
                    t = queue.Dequeue();
                }
            }
        }

        return t;
    }

    /// <summary>
    /// 对处理当前数据处理集合中的所有数据并行或顺序执行 execute 方法
    /// </summary>
    /// <typeparam name="T">数据类型参数</typeparam>
    /// <param name="data">处理集合</param>
    /// <param name="execute">处理方法</param>
    /// <param name="isParallel">是否并发执行, true 则并发执行, false 则顺序执行, 默认 false</param>
    /// <returns></returns>
    public static void Execute<T>(this IEnumerable<T> data, Action<T> execute, bool isParallel = false)
    {
        if (data != null && data.Any())
        {
            if (isParallel)
            {
                Parallel.ForEach(data, execute);
            }
            else
            {
                foreach (var t in data)
                {
                    execute(t);
                }
            }
        }
    }
    /// <summary>
    /// 对处理当前数据处理集合中的所有数据并行或顺序执行 execute 方法到终结点
    /// </summary>
    /// <typeparam name="T">数据类型参数</typeparam>
    /// <param name="data">处理集合</param>
    /// <param name="execute">处理方法</param>
    /// <param name="isParallel">是否并发执行, true 则并发执行, false 则顺序执行, 默认 false</param>
    /// <returns></returns>
    public static void ExecuteToEndpoint<T>(this IEnumerable<T> data, IEndpoint endpoint, Action<IEndpoint, T> execute, bool isParallel = false)
    {
        if (data != null && data.Any())
        {
            if (isParallel)
            {
                Parallel.ForEach(data, t => execute(endpoint, t));
            }
            else
            {
                foreach (var t in data)
                {
                    execute(endpoint, t);
                }
            }
        }
    }
    /// <summary>
    /// 对处理当前数据处理集合中的所有数据并行或顺序执行 execute 方法
    /// </summary>
    /// <typeparam name="T">数据类型参数</typeparam>
    /// <param name="data">处理集合</param>
    /// <param name="execute">处理方法</param>
    /// <param name="isParallel">是否并发执行, true 则并发执行, false 则顺序执行, 默认 false</param>
    /// <returns></returns>
    public static async Task ExecuteAsync<T>(this IEnumerable<T> data, Func<T, Task> execute, bool isParallel = false)
    {
        if (data != null && data.Any())
        {
            if (isParallel)
            {
                //由于 Parallel.ForEach 不支持异步并行, 因此使用任务组
                var tasks = new List<Task>();
                foreach (var t in data)
                {
                    tasks.Add(Task.Run(async () => await execute(t).ConfigureAwait(false)));
                }

                Task.WaitAll(tasks.ToArray());
            }
            else
            {
                foreach (var t in data)
                {
                    await execute(t).ConfigureAwait(false);
                }
            }
        }
    }
    /// <summary>
    /// 对处理当前数据处理集合中的所有数据并行或顺序执行 execute 方法到终结点
    /// </summary>
    /// <typeparam name="T">数据类型参数</typeparam>
    /// <param name="data">处理集合</param>
    /// <param name="execute">处理方法</param>
    /// <param name="isParallel">是否并发执行, true 则并发执行, false 则顺序执行, 默认 false</param>
    /// <returns></returns>
    public static async Task ExecuteToEndpointAsync<T>(this IEnumerable<T> data, IEndpoint endpoint, Func<IEndpoint, T, Task> execute, bool isParallel = false)
    {
        if (data != null && data.Any())
        {
            if (isParallel)
            {
                //由于 Parallel.ForEach 不支持异步并行, 因此使用任务组
                var tasks = new List<Task>();
                foreach (var t in data)
                {
                    tasks.Add(Task.Run(async () => await execute(endpoint, t).ConfigureAwait(false)));
                }

                Task.WaitAll(tasks.ToArray());
            }
            else
            {
                foreach (var t in data)
                {
                    await execute(endpoint, t).ConfigureAwait(false);
                }
            }
        }
    }
}